﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using NetCore.Common.Helper;
using NetCore.Model;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace NetCore.Common.Middleware
{
    public class CustomExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<CustomExceptionMiddleware> _logger;

        /// <summary>
        /// 计时器
        /// </summary>
        private Stopwatch _stopwatch;

        public CustomExceptionMiddleware(RequestDelegate next, ILogger<CustomExceptionMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            try
            {
                //请求参数
                string parameter = null;
                //是否加密
                if (AppSettings.Parameter)
                {
                    #region 参数初始化

                    httpContext.Request.EnableBuffering();
                    _stopwatch = new Stopwatch();
                    _stopwatch.Start();
                    _logger.LogInformation($"Handling request: " + httpContext.Request.Path);
                    var api = new ApiRequestInputViewModel
                    {
                        HttpType = httpContext.Request.Method,
                        Query = httpContext.Request.QueryString.Value,
                        RequestUrl = httpContext.Request.Path,
                        RequestName = "",
                        RequestIP = httpContext.Request.Host.Value
                    };

                    var request = httpContext.Request.Body;
                    var response = httpContext.Response.Body;

                    #endregion 参数初始化

                    //Post、Get请求
                    switch (httpContext.Request.Method)
                    {
                        case "POST":

                            #region Post请求参数返回参数进行加密

                            using (var newRequest = new MemoryStream())
                            {
                                //替换request流
                                httpContext.Request.Body = newRequest;

                                using var newResponse = new MemoryStream();
                                //替换response流
                                httpContext.Response.Body = newResponse;

                                using (var reader = new StreamReader(request))
                                {
                                    //读取原始请求流的内容
                                    api.Body = await reader.ReadToEndAsync();
                                    if (string.IsNullOrEmpty(api.Body))
                                        await _next.Invoke(httpContext);
                                    //示例加密字符串，使用 AES-ECB-PKCS7 方式加密，密钥为：0123456789abcdef
                                    // 加密参数：{"value":"哈哈哈"}
                                    // 加密后数据： oedwSKGyfLX8ADtx2Z8k1Q7+pIoAkdqllaOngP4TvQ4=
                                    api.Body = Cryptography.DecryptByAES(api.Body, AppSettings.ParameterKey, AppSettings.ParameterVI);//解密
                                }
                                using (var writer = new StreamWriter(newRequest))
                                {
                                    await writer.WriteAsync(api.Body);
                                    await writer.FlushAsync();
                                    newRequest.Position = 0;
                                    httpContext.Request.Body = newRequest;
                                    await _next(httpContext);
                                }

                                using (var reader = new StreamReader(newResponse))
                                {
                                    newResponse.Position = 0;
                                    api.ResponseBody = await reader.ReadToEndAsync();
                                    if (!string.IsNullOrWhiteSpace(api.ResponseBody))
                                    {
                                        api.ResponseBody = Cryptography.EncryptByAES(api.ResponseBody, AppSettings.ParameterKey, AppSettings.ParameterVI);//返回参数进行加密
                                    }
                                }
                                using (var writer = new StreamWriter(response))
                                {
                                    await writer.WriteAsync(api.ResponseBody);
                                    await writer.FlushAsync();
                                }
                            }

                            #endregion Post请求参数返回参数进行加密

                            break;

                        case "GET":

                            #region Get请求参数返回参数进行加密

                            //获取参数
                            string body = httpContext.Request.QueryString.Value;
                            //解密
                            api.Body = "?" + Cryptography.DecryptByAES(body[1..], AppSettings.ParameterKey, AppSettings.ParameterVI);

                            //赋值
                            QueryString queryString = new QueryString(api.Body);
                            httpContext.Request.QueryString = queryString;
                            await _next(httpContext);

                            #endregion Get请求参数返回参数进行加密

                            break;
                    }
                    parameter = api.Body;
                }
                else
                {
                    parameter = httpContext.Request.QueryString.Value;

                    await _next(httpContext);
                }

                #region 记录所有http请求

                //_logger.LogInformation("请求类型：" + httpContext.Request.Method + "\r\n" +
                //                       "请求地址：" + httpContext.Request.Path.Value + "\r\n" +
                //                       "请求参数：" + parameter + "\r\n" +
                //                       "请求ip：" + httpContext.Connection.RemoteIpAddress.ToString() + "\r\n" +
                //                       "请求时间：" + DateTime.Now + "\r\n" +
                //                       "获取UserAgent：" + httpContext.Request.Headers["User-Agent"].FirstOrDefault()
                //                       );

                #endregion 记录所有http请求
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message, ex); // 日志记录
                await HandleExceptionAsync(httpContext, ex.Message);
            }
            finally
            {
                var statusCode = httpContext.Response.StatusCode;
                var msg = "";
                switch (statusCode)
                {
                    case 401:
                        msg = "未授权";
                        break;

                    case 403:
                        msg = "拒绝访问";
                        break;

                    case 404:
                        msg = "未找到服务";
                        break;

                    case 405:
                        msg = "405 Method Not Allowed";
                        break;

                    case 502:
                        msg = "请求错误";
                        break;
                }
                if (!string.IsNullOrWhiteSpace(msg))
                    await HandleExceptionAsync(httpContext, msg);
            }
        }

        /// <summary>
        /// 错误返回信息
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        private async Task HandleExceptionAsync(HttpContext httpContext, string msg)
        {
            ErrorModel error = new ErrorModel
            {
                Code = httpContext.Response.StatusCode,
                Msg = msg
            };
            var result = JsonHelper.ToJson(error);
            httpContext.Response.ContentType = "application/json;charset=utf-8";
            await httpContext.Response.WriteAsync(result).ConfigureAwait(false);
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class CustomExceptionMiddlewareExtensions
    {
        public static IApplicationBuilder UseCustomExceptionMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<CustomExceptionMiddleware>();
        }
    }

    public class ApiRequestInputViewModel
    {
        public string HttpType { get; set; }
        public string Query { get; set; }
        public PathString RequestUrl { get; set; }
        public string RequestName { get; set; }
        public string RequestIP { get; set; }
        public string Body { get; set; }
        public string ResponseBody { get; set; }
        public long ElapsedTime { get; set; }
    }
}